package 中国.杨森.实验室.jpa;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.Tuple;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Fetch;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.Root;

import org.hibernate.LazyInitializationException;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

public class OM级联操作
{
	EntityManagerFactory emf;
	EntityManager em;

	@Before
	public void 启动()
	{
		emf = Persistence.createEntityManagerFactory("测试");
		em = emf.createEntityManager();
		em.getTransaction().begin();
	}

	@After
	public void 关闭()
	{
		em.getTransaction().commit();
		em.close();
		emf.close();
	}

	private void 变更事务()
	{
		关闭事务();
		开启事务();
	}

	private void 关闭事务()
	{
		em.getTransaction().commit();
		em.close();
		System.out.println("事物关闭-----------");
	}

	private void 开启事务()
	{
		em = emf.createEntityManager();
		em.getTransaction().begin();
		System.out.println("事物开启-----------");
	}

	private void 关联学生班级(Integer 班级ID, Integer... 学生IDs)
	{
		班级 bj = em.find(班级.class, 班级ID);
		for (int i = 0; i < 学生IDs.length; i++)
		{
			学生 xs = em.find(学生.class, 学生IDs[i]);
			xs.set所在班级(bj);
		}
	}

	private void 增学生(int size)
	{
		for (int i = 0; i < size; i++)
		{
			学生 xs = new 学生();
			xs.set姓名("学生" + i);
			xs.set年龄((short) i);
			em.persist(xs);
		}
	}

	private void 增班级(int size)
	{
		for (int i = 0; i < size; i++)
		{
			班级 bj = new 班级();
			bj.set名("班级" + i);
			em.persist(bj);
		}
	}

	@Test
	/**
	 * 此处不含one一方未保存情况，因为这种情况过于依赖框架
	 */
	public void 增()
	{
		增班级(2);

		班级 bj = em.find(班级.class, 2);

		学生 xs = new 学生();
		xs.set姓名("学生x");
		xs.set年龄((short) 10);
		xs.set所在班级(bj);
		em.persist(xs);
	}

	@Test
	public void 删M()
	{
		增学生(5);
		增班级(2);
		关联学生班级(1, 1, 2, 3);

		变更事务();

		学生 xs = em.find(学生.class, 1);
		em.remove(xs);
	}

	@Test
	public void 删O()
	{
		增学生(5);
		增班级(2);
		关联学生班级(1, 1, 2);

		变更事务();

		学生 xs = em.find(学生.class, 1);
		em.remove(xs);
		学生 xs2 = em.find(学生.class, 2);
		em.remove(xs2);

		班级 bj = em.find(班级.class, 1);
		em.remove(bj);
	}

	@Test
	public void 改()
	{
		增学生(5);
		增班级(2);
		关联学生班级(1, 1, 2, 3);

		变更事务();

		关联学生班级(2, 2, 4);
	}

	@Test
	public void 查_先M在O()
	{
		增学生(5);
		增班级(2);
		关联学生班级(1, 1, 2, 3);

		变更事务();

		学生 xs = em.find(学生.class, 1);
		em.getTransaction().commit();
		em.close();

		System.out.println("事物关闭-----------");
		System.out.println(xs.get所在班级().getId());
		System.out.println("事物开启-----------");
		em = emf.createEntityManager();
		em.getTransaction().begin();

		班级 bj = em.find(班级.class, xs.get所在班级().getId());
		System.out.println(bj.get名());
	}

	@Test
	public void 查_先O在M()
	{
		增学生(5);
		增班级(2);
		关联学生班级(1, 1, 2, 3);

		变更事务();

		班级 bj = em.find(班级.class, 1);

		关闭事务();

		boolean 成功执行 = true;
		try
		{
			System.out.println(bj.get所有学生().size());
		}
		catch (LazyInitializationException e)
		{
			成功执行 = false;
			System.out.println("不可以执行");
		}
		finally
		{
			if (成功执行)
			{
				throw new RuntimeException("此处能执行才是异常");
			}
		}

		开启事务();

		CriteriaBuilder cb = em.getCriteriaBuilder();
		CriteriaQuery<学生> cq = cb.createQuery(学生.class);
		Root<学生> root = cq.from(学生.class);
		cq.select(root);
		cq.where(cb.equal(root.get(学生_.所在班级), bj));
		List<学生> list = em.createQuery(cq).getResultList();
		for (int i = 0; i < list.size(); i++)
		{
			System.out.println(list.get(i).get姓名());
		}
	}

	@Test
	public void 查M_以O的属性作为搜索条件()
	{
		增学生(5);
		增班级(2);
		关联学生班级(1, 1, 2, 3);

		变更事务();

		CriteriaBuilder cb = em.getCriteriaBuilder();
		CriteriaQuery<学生> cq = cb.createQuery(学生.class);
		Root<学生> root = cq.from(学生.class);
		cq.select(root);
		 Fetch<学生, 班级> f = root.fetch(学生_.所在班级);
//		Join<学生, 班级> join班级 = root.join(学生_.所在班级);
		cq.where(cb.equal(root.get(学生_.所在班级).get(班级_.名), "班级0"));

		List<学生> list = em.createQuery(cq).getResultList();
		for (int i = 0; i < list.size(); i++)
		{
			System.out.println(list.get(i).get姓名());
		}
		关闭事务();

		boolean 成功执行 = true;
		try
		{
			System.out.println(list.get(0).get所在班级().get名());
		}
		catch (LazyInitializationException e)
		{
			成功执行 = false;
			System.out.println("不可以执行");
		}
		finally
		{
			if (成功执行)
			{
				throw new RuntimeException("此处能执行才是异常");
			}
		}

		开启事务();
	}

	@Test
	// 总觉得实现不合理,当前方法在返回时额外生成了一些无用对象
	// 上面的方法中，只要把注释掉的root.fetch(学生_.所在班级)启用，即可正常使用，但是sql查询多join了一张表
	// 综合考虑，采用当前方法
	public void 查M和O_以O的属性作为搜索条件()
	{
		增学生(6);
		增班级(2);

		关联学生班级(1, 1, 3);
		关联学生班级(2, 2, 4);

		变更事务();

		CriteriaBuilder cb = em.getCriteriaBuilder();
		CriteriaQuery<Tuple> cq = cb.createQuery(Tuple.class);
		Root<学生> root = cq.from(学生.class);
		Join<学生, 班级> join班级 = root.join(学生_.所在班级);
		cq.multiselect(root, join班级);
		cq.where(cb.or(cb.equal(join班级.get(班级_.名), "班级0"), cb.equal(join班级.get(班级_.名), "班级1")));
		cq.orderBy(cb.asc(root.get(学生_.id)));
		List<Tuple> list = em.createQuery(cq).getResultList();
		for (int i = 0; i < list.size(); i++)
		{
			System.out.println(list.get(i).get(root).get姓名());
			System.out.println(list.get(i).get(join班级).get名());
		}
		关闭事务();
		System.out.println(list.get(0).get(root).get所在班级().get名());
		System.out.println(list.get(1).get(root).get所在班级().get名());
		开启事务();
	}
}